using System;
using CI.Misc;
using CI.Organism;
using NUnit.Framework;
using CI.Interfaces;

namespace Mesopotamia.UnitTests.Neural
{
    [TestFixture]
    public class NeuralTests
    {
        // neuron to use
        SynapseIO _synapseIO;
        CI.Organism.Organism _organism;
        SimpleNeuralSystem _sns;
        IHostingEnvironment _hostingEnvironment;
        StepNeuron _neuron1;
        StepNeuron _neuron2;
        StepNeuron _neuron3;
        SimpleSynapse _inputSynapse1;
        SimpleSynapse _inputSynapse2;
        SimpleSynapse _outputSynapse1;
        SimpleSynapse _outputSynapse2;
        SimpleSynapse _outputSynapse3;
        SimpleSynapse _outputSynapse4;
        SimpleSynapse _outputSynapse5;

        [SetUp]
        public void Setup()
        {
            _hostingEnvironment = new CI.Mesopotamia.MSRBHost(new MockHostingEnvironmentPhysical(), new CI.Mesopotamia.ScenarioLocalXML());
            _organism = new CI.Organism.Organism(_hostingEnvironment);
            _sns = new SimpleNeuralSystem(_organism);
            _synapseIO = new SynapseIO("bindingName", "shortName", "description", Guid.NewGuid());
            _neuron1 = new StepNeuron(1, _sns, Guid.NewGuid());
            _neuron2 = new StepNeuron(5, _sns, Guid.NewGuid());
            _neuron3 = new StepNeuron(1, _sns, Guid.NewGuid());
            _inputSynapse1 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _inputSynapse2 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _outputSynapse1 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _outputSynapse2 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _outputSynapse3 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _outputSynapse4 = new SimpleSynapse(Guid.NewGuid(), _sns);
            _outputSynapse5 = new SimpleSynapse(Guid.NewGuid(), _sns);
        }
        /// <summary>guid null argument exception checking</summary>
        [Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "guid")]
        public void CtorNeuronNullGuid()
        {
            new StepNeuron(0, _sns, Guid.Empty);
        }

        /// <summary>
        /// Neural network simple echo's its inputs through a neuron then read on a synapse on its way out
        /// </summary>
        [Test]
        public void CurrentDisplayValue_EchoNeuralSystem_AreEqual()
        {
            // setup all the varibles
            const int inputStart = 100;

            _neuron1.InputSynapse.Add(_inputSynapse1);
            _neuron1.OutputSynapse.Add(_outputSynapse1);

            _inputSynapse1.AddSynapticConnectionTarget(_neuron1);
            _outputSynapse1.AddSynapticConnectionSource(_neuron1);

            _outputSynapse1.AddSynapticConnectionTarget(_synapseIO);

            // wire it up
            _sns.AddNeuron(_neuron1);

            // send something through            
            _inputSynapse1.FireInput(inputStart);
            _sns.Evaluate();


            // make sure we get it back out
            Assert.AreEqual(inputStart, _synapseIO.CurrentDisplayValue);

        }

        [Test]
        public void CurrentDisplayValue_TwoLayerFeedForwardNN_Equal()
        {
            // setup all the varibles
            const int inputStart = 200;

            _neuron1.InputSynapse.Add(_inputSynapse1);
            _neuron1.OutputSynapse.Add(_outputSynapse1);

            _inputSynapse1.AddSynapticConnectionTarget(_neuron1);
            
            _outputSynapse1.AddSynapticConnectionSource(_neuron1);
            _outputSynapse1.AddSynapticConnectionTarget(_neuron2);
            
            _neuron2.InputSynapse.Add(_outputSynapse1);
            _neuron2.OutputSynapse.Add(_outputSynapse2);

            _outputSynapse2.AddSynapticConnectionSource(_neuron2);
            _outputSynapse2.AddSynapticConnectionTarget(_synapseIO);

            // wire it up
            _sns.AddNeuron(_neuron1);
            _sns.AddNeuron(_neuron2);

            // send something through            
            _inputSynapse1.FireInput(inputStart);
            _sns.Evaluate();
            Assert.AreEqual(inputStart, _synapseIO.CurrentDisplayValue);
        }


        /// <summary>
        /// Neural network that counts up to 100
        /// </summary>
        [Test, Ignore("To Fix")]
        public void CountingBase2NeuralSystem()
        {
            // setup all the varibles
            const int inputStart = 1;
            _inputSynapse2.InitInputWeight = -10;

            // setup neuron 1
            _neuron1.InputSynapse.Add(_inputSynapse1);
            _inputSynapse1.AddSynapticConnectionTarget(_neuron1);
            _neuron1.InputSynapse.Add(_outputSynapse2);
            _outputSynapse2.AddSynapticConnectionTarget(_neuron1);
            _neuron1.InputSynapse.Add(_outputSynapse5);
            _outputSynapse5.AddSynapticConnectionTarget(_neuron1);
            _neuron1.InputSynapse.Add(_inputSynapse2);
            _inputSynapse2.AddSynapticConnectionTarget(_neuron1);

            _neuron1.OutputSynapse.Add(_outputSynapse1);
            _outputSynapse1.AddSynapticConnectionSource(_neuron1);
            _neuron1.OutputSynapse.Add(_outputSynapse3);
            _outputSynapse3.AddSynapticConnectionSource(_neuron1);

            // setup neuron 2
            _neuron2.InputSynapse.Add(_outputSynapse3);
            _outputSynapse3.AddSynapticConnectionTarget(_neuron2);
            _neuron2.OutputSynapse.Add(_outputSynapse4);
            _outputSynapse4.AddSynapticConnectionSource(_neuron2);
            _neuron2.OutputSynapse.Add(_inputSynapse2);
            _inputSynapse2.AddSynapticConnectionSource(_neuron2);

            // setup neuron 3
            _neuron3.InputSynapse.Add(_outputSynapse1);
            _outputSynapse1.AddSynapticConnectionTarget(_neuron3);
            _neuron3.OutputSynapse.Add(_outputSynapse2);
            _outputSynapse2.AddSynapticConnectionSource(_neuron3);
            _neuron3.OutputSynapse.Add(_outputSynapse5);
            _outputSynapse5.AddSynapticConnectionSource(_neuron3);

            // wire it up
            _sns.AddNeuron(_neuron1);
            _sns.AddNeuron(_neuron2);
            _sns.AddNeuron(_neuron3);

            // send something through            
            _inputSynapse1.FireInput(inputStart);
            _sns.Evaluate();
            _outputSynapse4.Evaluate(99);
            Assert.AreEqual(8, _outputSynapse1.OutputValue);

        }

    }
}
